// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Package filelock为建议文件
// 锁定提供了独立于平台的API。在不支持
// 建议锁的平台上调用此包中的函数将返回错误，IsNotSupported将返回true。
package filelock

import (
	"errors"
	"io/fs"
	"os"
)

// 文件提供了锁定打开文件所需的最少方法集。
// 文件实现必须可用作映射键。
// 通常的实现是*os。文件
type File interface {
	// Name返回文件名。
	Name() string

	// Fd返回一个有效的文件描述符。
	// （如果文件是*os.File，则不能关闭。）
	Fd() uintptr

	// Stat返回描述文件的FileInfo结构。
	Stat() (fs.FileInfo, error)
}

// Lock在文件上放置一个建议写锁，直到可以锁定为止。
// 
// 如果Lock返回nil，则在该进程退出、关闭f或调用Unlock之前，任何其他进程都无法对该文件放置读或写
// 锁。
// 
// 如果f的描述符已被读或写锁定，则锁定行为为
// 未指定。
// 
// 关闭文件可能会或可能不会立即释放锁。呼叫者应
// 确保在锁定成功时始终调用Unlock。
func Lock(f File) error {
	return lock(f, writeLock)
}

// RLock对文件设置一个建议读取锁定，直到可以锁定为止。
// 
// 如果RLock返回nil，则在该进程退出、关闭f或调用Unlock之前，任何其他进程都无法对该文件设置写锁。
// 
// 如果f已经读写锁定，则RLock的行为未指定。
// 
// 关闭文件可能会或可能不会立即释放锁。呼叫者应
// 确保在RLock成功时始终调用Unlock。
func RLock(f File) error {
	return lock(f, readLock)
}

// Unlock将删除通过此过程放置在f上的建议锁。
// 
// 调用者不得尝试解锁未锁定的文件。
func Unlock(f File) error {
	return unlock(f)
}

// String返回与lt 
// （Lock、RLock或Unlock）对应的函数名。
func (lt lockType) String() string {
	switch lt {
	case readLock:
		return "RLock"
	case writeLock:
		return "Lock"
	default:
		return "Unlock"
	}
}

// IsNotSupported返回一个布尔值，指示
// 是否知道该错误，并报告不支持某个函数（可能针对特定输入）。
// 它满足于ErrNotSupported以及一些系统调用错误。
func IsNotSupported(err error) bool {
	return isNotSupported(underlyingError(err))
}

var ErrNotSupported = errors.New("operation not supported")

// underyingError返回已知操作系统错误类型的基础错误。
func underlyingError(err error) error {
	switch err := err.(type) {
	case *fs.PathError:
		return err.Err
	case *os.LinkError:
		return err.Err
	case *os.SyscallError:
		return err.Err
	}
	return err
}
